package org.sgmiller.quickstem.collections;

import java.util.AbstractList;

import org.sgmiller.quickstem.Stem;
import org.sgmiller.quickstem.Transaction;

abstract class AbstractTransactionalList<V> extends AbstractList<V> {

	static class TxnListEntry<V> {
		
		Stem<TxnListEntry<V>> next=new Stem<TxnListEntry<V>>();
		Stem<V> value=new Stem<V>();
		
		TxnListEntry(Transaction current, V value) {
			setValue(current, value);
		}
		
		V getValue(Transaction current) {
			return current.get(value);
		}
		
		void setValue(Transaction current, V value) {
			this.value.set(current, value);
		}
		
		TxnListEntry<V> getNext(Transaction current) {
			return current.get(next);
		}
		
		void setNext(Transaction current, TxnListEntry<V> next) {
			this.next.set(current, next);
		}		
	}
	
	static final Object NO_VALUE=new Object();
	
	Stem<TxnListEntry<V>> 
		head=new Stem<TxnListEntry<V>>(),
		tail=new Stem<TxnListEntry<V>>();
	
	@Override
	public V get(int index) {
		Transaction txn=Transaction.current();

		if (isEmpty())
			throw new IndexOutOfBoundsException();
		
		TxnListEntry<V> current=txn.get(head);
		do {
			if (0 == index) {
				return current.getValue(txn);
			}
			index--;
			current=current.getNext(txn);
		} while (current != null);
		throw new IndexOutOfBoundsException();
	}
	
	@Override
	public V set(int index, V value) {
		Transaction txn=Transaction.current();

		if (isEmpty())
			throw new IndexOutOfBoundsException();
		
		TxnListEntry<V> current=txn.get(head);
		do {
			if (0 == index) {
				V old=current.getValue(txn);
				current.setValue(txn, value);
				return old;
			}
			index--;
			current=current.getNext(txn);
		} while (current != null);
		throw new IndexOutOfBoundsException();
	}

	@Override
	public int size() {
		Transaction txn=Transaction.current();

		int size=0;
		TxnListEntry<V> current=txn.get(head);
		while (current != null) {
			size++;
			current=current.getNext(txn);
		}
		return size;
	}
	

	 public boolean add(V value) {
		 Transaction txn=Transaction.current();

		 TxnListEntry<V> tl=txn.get(tail);
		 TxnListEntry<V> nv=new TxnListEntry<V>(txn, value);
		 if (tl==null) {
			 head.set(txn, nv);
			 tail.set(txn, nv);
		 } else {
			 tl.setNext(txn, nv);
		 }
		 return true;
	 }

	 public V remove(int idx) {
		 Transaction txn=Transaction.current();

		 TxnListEntry<V> current = txn.get(head);
		 TxnListEntry<V> last = null;
		 while (current != null && idx>0) {
			idx--;
			last=current;
			current = current.getNext(txn);
		 }
		 if (current==null) throw new IndexOutOfBoundsException();

		 V old=current.getValue(txn);
		 if (current.getNext(txn) == null) {
			 if (last==null) {
				 head.set(txn, null);
				 tail.set(txn, null);
			 } else {
				 last.setNext(txn, null);
				 tail.set(txn, last);
			 }
		 } else {
			 if (last != null) {
				 last.setNext(txn, current.getNext(txn));			 				 
			 } else {
				 head.set(txn, current.getNext(txn));				 
			 }
		 }
		 return old;
	 }
}
